#include "shmem_msg_fifo.h"

#include <stdlib.h>
#include <string.h>
#include <algorithm>

#include "sem_posix.h"

using namespace std;

static const int MSG_HEADER_LEN = 4;

CShmemMsgFifo::CShmemMsgFifo(CShmem* pshmem,CSem* psem)
    : m_shmem(pshmem),m_sem(psem),m_size(0)
{
    if (m_shmem == NULL || m_sem == NULL)
        throw string("CshmemMsgFifo constrcutor error");

    if (!m_sem->open())
        throw string("CshmemMsgFifo constrcutor sem error");
    
    if (!m_shmem->open())
        throw string("CshmemMsgFifo constrcutor shmem error");
    
    char* head = (char*)m_shmem->get();
    if (head != NULL)
    {
        m_size      = m_shmem->size();
        if (m_size < sizeof(MemMsgHeader))
            throw string("shmem size too small");
        // 共享内存已经初始化，即都 memset 为 0 了
        // 总大小中，减去header所占用的
        m_size -= sizeof(MemMsgHeader);
    }
}

bool CShmemMsgFifo::push(void* msg,unsigned int len)
{
    if (len < 0)
        return false;

    CScopedSem semlock(m_sem);
    char* head = (char*)m_shmem->get();
    if (head == NULL)
        return false;
    
    // 主要防止第二轮写会覆盖 没pop完的msg
    MemMsgHeader* memheader = (MemMsgHeader*) head;
    int offset = sizeof(MemMsgHeader);
    if ((memheader->m_pushedsize > memheader->m_popedsize)
            // 当统计值相等时，要判断是不是第二次写
            || (memheader->m_pushedsize == memheader->m_popedsize
                && memheader->m_endsize == 0)
       )
    {
        // 还没存满
        if (len + MSG_HEADER_LEN + memheader->m_pushedsize <= m_size)
        {
            offset += memheader->m_pushedsize;
            memcpy(head + offset, &len, MSG_HEADER_LEN);
            offset += MSG_HEADER_LEN;
            memcpy(head + offset, msg, len);
            // 更新共享内存中的元数据
            memheader->m_pushedsize += len + MSG_HEADER_LEN;
            memheader->m_msgnum += 1;
            return true;
        }
        // 存满了，从头开始存
        else if (memheader->m_popedsize >= len + MSG_HEADER_LEN )
        {
            // 记录消息队列结束位置
            memheader->m_endsize = memheader->m_pushedsize;
            memcpy(head + offset, &len, MSG_HEADER_LEN);
            offset += MSG_HEADER_LEN;
            memcpy(head + offset, msg, len);
            // 重置 pushedsize
            memheader->m_pushedsize = len + MSG_HEADER_LEN;
            memheader->m_msgnum += 1;
            return true;
        }
        else
        {
            return false;
        }
    }
    else if (memheader->m_pushedsize < memheader->m_popedsize)
    {
        if (len + MSG_HEADER_LEN + memheader->m_pushedsize <= memheader->m_popedsize)
        {
            offset += memheader->m_pushedsize;
            // 写长度
            memcpy(head + offset, &len, MSG_HEADER_LEN);
            // 写内容
            offset += MSG_HEADER_LEN;
            memcpy(head + offset, msg, len);
            // 更新共享内存中的元数据
            memheader->m_pushedsize += len + MSG_HEADER_LEN;
            memheader->m_msgnum += 1;
            return true;
        }
        else
        {
            return false;
        }
    }

    return false;
}

unsigned int CShmemMsgFifo::pop(char** buf)
{
    CScopedSem semlock(m_sem);
    char* head = (char*)m_shmem->get();
    if (head == NULL)
        return 0;
    
    MemMsgHeader* memheader = (MemMsgHeader*) head;
    if (memheader->m_msgnum == 0)
        return 0;

    int offset = sizeof(MemMsgHeader);
    unsigned int len = 0;
    if ( (memheader->m_endsize == 0 && memheader->m_popedsize < memheader->m_pushedsize)
            || memheader->m_popedsize < memheader->m_endsize)
    {
        offset += memheader->m_popedsize;
        memcpy(&len,head + offset,MSG_HEADER_LEN);
        if (len > m_size)
            return 0;
        offset += MSG_HEADER_LEN;
        *buf = (char*)malloc(len);
        if (*buf == NULL)
            return 0;
        memcpy(*buf,head + offset,len);
        // 更新统计值
        memheader->m_popedsize += MSG_HEADER_LEN + len;
        memheader->m_msgnum -= 1;
        return len;
    }
    else if (memheader->m_endsize == memheader->m_popedsize)
    {
        if (memheader->m_pushedsize >= len + MSG_HEADER_LEN)
        {
            memcpy(&len,head + offset,MSG_HEADER_LEN);
            if (len > m_size)
                return 0;
            *buf = (char*)malloc(len);
            if (*buf == NULL)
                return 0;
            // 从头开始读
            offset += MSG_HEADER_LEN;
            memcpy(*buf,head + offset,len);
            // 更新统计值
            memheader->m_popedsize = MSG_HEADER_LEN + len;
            memheader->m_endsize = 0;
            memheader->m_msgnum -= 1;
            return len;
        }
    }

    return 0;
}











